package com.doubao.common.utils;

import io.lettuce.core.ClientOptions;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.TimeoutOptions;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Supplier;

/**
 * Redis连接工厂工具类 - 优化版
 * 提供预配置的RedisTemplate实例，具有自动重连和故障转移功能
 */
@Slf4j
@Component
public class RedisTemplateFactory {

    private static final String REDIS_HOST = "192.168.194.206";
//    private static final String REDIS_HOST = "127.0.0.1";
    private static final int REDIS_PORT = 6379;
    private static final int REDIS_DATABASE = 0;
//    private static final String REDIS_PASSWORD = "";
    private static final String REDIS_PASSWORD = "DoubaoAuthServiceJwtSecretKey0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    // 连接参数 - 优化超时设置
    private static final int CONNECT_TIMEOUT = 1000; // 连接超时时间（毫秒）- 减少等待时间
    private static final int COMMAND_TIMEOUT = 500; // 命令执行超时时间（毫秒）- 减少等待时间
    private static final int MAX_RETRY_ATTEMPTS = 2; // 最大重试次数 - 减少重试次数，更快失败

    // 默认RedisTemplate实例
    private static volatile RedisTemplate<String, Object> objectRedisTemplate;
    private static volatile RedisTemplate<String, String> stringRedisTemplate;

    // 用于记录连接状态
    private static volatile boolean connectionHealthy = true;
    private static volatile long lastFailureTime = 0;
    private static final long RECOVERY_WAIT_TIME = TimeUnit.SECONDS.toMillis(30); // 30秒恢复等待时间 - 减少恢复等待时间

    // 用于控制重连线程
    private static final AtomicBoolean reconnecting = new AtomicBoolean(false);
    private static ClientResources clientResources;

    static {
        initRedisTemplates();
    }

    /**
     * 初始化RedisTemplate实例
     */
    private static void initRedisTemplates() {
        try {
            // 创建和配置客户端资源
            clientResources = DefaultClientResources.builder()
                    .ioThreadPoolSize(4)
                    .computationThreadPoolSize(4)
                    .build();

            // 创建配置
            RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
            redisConfig.setHostName(REDIS_HOST);
            redisConfig.setPort(REDIS_PORT);
            redisConfig.setDatabase(REDIS_DATABASE);
            redisConfig.setPassword(REDIS_PASSWORD);

            // 创建连接工厂
            LettuceConnectionFactory factory = createConnectionFactory(redisConfig, clientResources);

            // 初始化String, Object类型的RedisTemplate
            objectRedisTemplate = createRedisTemplate(factory, new GenericJackson2JsonRedisSerializer());

            // 初始化String, String类型的RedisTemplate
            stringRedisTemplate = createRedisTemplate(factory, new StringRedisSerializer());

            connectionHealthy = true;
            log.info("初始化RedisTemplate成功，连接到 {}:{}", REDIS_HOST, REDIS_PORT);
        } catch (Exception e) {
            // 初始化失败，立即标记为不健康
            markConnectionFailure(e);

            // 创建空模板，避免NPE
            createEmptyTemplates();

            // 不在静态初始化中启动重连，避免阻塞应用启动
            // 首次获取模板时会触发重连
        }
    }

    /**
     * 创建空模板以避免NPE
     */
    private static void createEmptyTemplates() {
        // 创建一个假的连接工厂，不会实际连接Redis
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName("localhost");
        redisConfig.setPort(6379);

        LettuceConnectionFactory dummyFactory = new LettuceConnectionFactory(redisConfig);
        dummyFactory.afterPropertiesSet();

        objectRedisTemplate = createRedisTemplate(dummyFactory, new GenericJackson2JsonRedisSerializer());
        stringRedisTemplate = createRedisTemplate(dummyFactory, new StringRedisSerializer());
    }

    /**
     * 创建Lettuce连接工厂
     */
    private static LettuceConnectionFactory createConnectionFactory(
            RedisStandaloneConfiguration redisConfig, ClientResources clientResources) {

        // 配置Socket选项
        SocketOptions socketOptions = SocketOptions.builder()
                .connectTimeout(Duration.ofMillis(CONNECT_TIMEOUT))
                .keepAlive(true)
                .tcpNoDelay(true)
                .build();

        // 配置客户端选项
        ClientOptions clientOptions = ClientOptions.builder()
                .socketOptions(socketOptions)
                .timeoutOptions(TimeoutOptions.enabled(Duration.ofMillis(COMMAND_TIMEOUT)))
                .autoReconnect(true)
                .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS)
                .build();

        // 创建Lettuce客户端配置
        LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder()
                .clientResources(clientResources)
                .clientOptions(clientOptions)
                .commandTimeout(Duration.ofMillis(COMMAND_TIMEOUT))
                .build();

        // 创建连接工厂
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig, clientConfig);
        factory.afterPropertiesSet();

        return factory;
    }

    /**
     * 创建并配置RedisTemplate
     */
    private static <T> RedisTemplate<String, T> createRedisTemplate(LettuceConnectionFactory factory,
                                                                    org.springframework.data.redis.serializer.RedisSerializer valueSerializer) {
        RedisTemplate<String, T> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        template.setKeySerializer(stringRedisSerializer);
        template.setHashKeySerializer(stringRedisSerializer);

        // 设置value的序列化方式
        template.setValueSerializer(valueSerializer);
        template.setHashValueSerializer(valueSerializer);

        template.afterPropertiesSet();
        return template;
    }

    /**
     * 安排重连任务
     */
    private static void scheduleReconnection() {
        if (reconnecting.compareAndSet(false, true)) {
            log.info("启动Redis重连后台线程");
            Thread reconnectThread = new Thread(() -> {
                try {
                    // 立即尝试重连，不等待
                    int attempts = 0;
                    boolean reconnected = false;

                    while (!reconnected && attempts < MAX_RETRY_ATTEMPTS) {
                        attempts++;
                        try {
                            log.info("尝试重新连接Redis (尝试 {}/{})", attempts, MAX_RETRY_ATTEMPTS);

                            // 创建配置
                            RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
                            redisConfig.setHostName(REDIS_HOST);
                            redisConfig.setPort(REDIS_PORT);
                            redisConfig.setDatabase(REDIS_DATABASE);
                            redisConfig.setPassword(REDIS_PASSWORD);

                            // 创建连接工厂
                            LettuceConnectionFactory factory = createConnectionFactory(redisConfig, clientResources);

                            // 重新初始化模板
                            objectRedisTemplate = createRedisTemplate(factory, new GenericJackson2JsonRedisSerializer());
                            stringRedisTemplate = createRedisTemplate(factory, new StringRedisSerializer());

                            // 测试连接 - 使用更简单的检查方式，并设置短超时
                            try {
                                factory.getConnection().ping();
                                connectionHealthy = true;
                                reconnected = true;
                                log.info("Redis重连成功");
                            } catch (Exception e) {
                                throw new RuntimeException("连接测试失败", e);
                            }
                        } catch (Exception e) {
                            log.error("Redis重连尝试失败 (尝试 {}/{}): {}", attempts, MAX_RETRY_ATTEMPTS, e.getMessage());
                            if (attempts < MAX_RETRY_ATTEMPTS) {
                                // 固定短时间重试，不用指数退避
                                Thread.sleep(500);
                            }
                        }
                    }

                    if (!reconnected) {
                        log.error("Redis重连失败，达到最大重试次数 ({})", MAX_RETRY_ATTEMPTS);
                        lastFailureTime = System.currentTimeMillis();
                    }
                } catch (InterruptedException e) {
                    log.error("Redis重连线程被中断", e);
                    Thread.currentThread().interrupt();
                } finally {
                    reconnecting.set(false);
                }
            });
            reconnectThread.setDaemon(true);
            reconnectThread.setName("redis-reconnect-thread");
            reconnectThread.start();
        }
    }

    /**
     * 检查连接状态并尝试恢复
     */
    private static void checkConnection() {
        if (!connectionHealthy) {
            // 如果连接不健康，且超过恢复等待时间，尝试重连
            if (System.currentTimeMillis() - lastFailureTime > RECOVERY_WAIT_TIME &&
                    !reconnecting.get()) {
                scheduleReconnection();
            }
        }
    }

    /**
     * 标记连接失败并启动恢复流程
     */
    private static void markConnectionFailure(Exception e) {
        if (connectionHealthy) {
            connectionHealthy = false;
            lastFailureTime = System.currentTimeMillis();
            log.warn("Redis连接标记为不健康: {}", e.getMessage());

            // 不在这里启动重连，而是由checkConnection来控制重连时机
        }
    }

    /**
     * 获取Redis连接状态
     */
    public static boolean isConnectionHealthy() {
        return connectionHealthy;
    }

    /**
     * 获取<String, Object>类型的RedisTemplate实例
     */
    public static RedisTemplate<String, Object> getRedisTemplate() {
        //检查redis连接是否正常
        checkConnection();
        return objectRedisTemplate;
    }

    /**
     * 获取<String, String>类型的RedisTemplate实例
     */
    public static RedisTemplate<String, String> getStringRedisTemplate() {
        checkConnection();
        return stringRedisTemplate;
    }

    /**
     * 安全执行Redis操作，提供错误处理和降级策略
     * @param redisOperation Redis操作
     * @param fallback 降级操作
     * @param <T> 返回类型
     * @return 操作结果
     */
    public static <T> T executeWithFallback(RedisOperation<T> redisOperation, Supplier<T> fallback) {
        if (!connectionHealthy) {
            // 如果连接已知不健康，直接使用降级策略
            return fallback.get();
        }

        try {
            T result = redisOperation.execute();
            return result;
        } catch (Exception e) {
            log.warn("Redis操作异常，使用降级策略: {}", e.getMessage());
            markConnectionFailure(e);
            return fallback.get();
        }
    }

    /**
     * Redis操作接口
     */
    @FunctionalInterface
    public interface RedisOperation<T> {
        T execute() throws Exception;
    }
}